﻿/* 
* Copyright (c) 2009, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Bio.Data.Interfaces;
using Bio.Data.Interfaces.Drawing;
using Bio.Data.Providers.Structure.XRNA.Internal;
using LabelRNA;

namespace Bio.Data.Providers.Structure.XRNA
{
    /// <summary>
    /// This loads the XRNA file format
    /// </summary>
    class XRNAFile : BioFileDataLoader<IStructureModelBioEntity>
    {
        protected override bool OnInitialize()
        {
            if (base.OnInitialize())
            {
                _sequence = new SimpleRNASequence() { Id = string.Empty, SourceId = string.Empty, ScientificName = string.Empty };
                _sequence.SetExtendedProperty(SequenceMetadata.Key, new SequenceMetadata());
                _lines = new List<string>();
                _basePairs = new List<IBasePairBioEntity>();
                _extents = new List<IStructureExtentBioEntity>();
                _rendering = new SequenceRendering();
                _strModelMetadata = new StructureModelMetadata();

                using (var reader = File.OpenText(InitializationData))
                {
                    string line;
                    while (!string.IsNullOrEmpty(line = reader.ReadLine())) {
                        _lines.Add(line);
                    }
                }
                return true;
            }
            return false;
        }

        protected override void OnLoad()
        {
            //1. Load the basic elements (Sequence, Base Pairs, Extents and Rendering)
            if (!LoadStructureModel()) return;

            //2. Wire up the structure model
            _sequence.GetExtendedProperty<SequenceMetadata>(SequenceMetadata.Key).Data.Add(SequenceRendering.Key, _rendering);
            RNASecondaryStructureModel model = new RNASecondaryStructureModel(InitializationData, _sequence);
            model.BasePairs = _basePairs;
            model.Extents = _extents;
            model.SetExtendedProperty(StructureModelMetadata.Key, _strModelMetadata);

            //3. Add the structure model to the entity set.
            Entities.Add(model);

            //4. Load any font rendering specifications and visibility for Sequence elements;
            HandleSequenceElements();

            //5. Process all annotations/labels
            LoadAnnotations();
        }

        #region Private Members and Methods

        /// <summary>
        /// In this method, we load all drawing annotations including:
        /// Base Pair Connectors
        /// Lines
        /// Arcs
        /// Rectangles
        /// Arrows
        /// Text
        /// 
        /// If we fail parsing any annotation, we just skip it, eventually that should
        /// be logged.
        /// </summary>
        /// <returns></returns>
        private void LoadAnnotations()
        {
            HandleBasePairConnectors();
        }

        /// <summary>
        /// Load any specific renderings for base pair connectors
        /// </summary>
        private void HandleBasePairConnectors()
        {
            //1. Lets see if we have any base pair connectors defined
            foreach (string line in _lines)
            {
                var basePairConnector = XRNABasePairConnectorShape.Create(line);
                if (basePairConnector != null)
                {
                    //Use a simple LINQ query to identify the base pairs that this connector should be assigned to
                    var basePairs = from bp in _basePairs
                                    from fpIdx in basePairConnector.AssignedTo
                                    where bp.FivePrimeIndex == fpIdx
                                    select bp;
                    if (basePairs.Count() == _basePairs.Count()) //Then we want to put it in the StructureModelMetadata
                    {
                        _strModelMetadata.ConnectorVisible = true;
                        _strModelMetadata.ConnectorRendering = basePairConnector.ConnectorVisual;
                        if (basePairConnector.Style.HasValue)
                            _strModelMetadata.ConnectorStyle = basePairConnector.Style.Value;
                    }
                    else
                    {
                        foreach (var basePairForConnector in basePairs)
                        {
                            IExtensibleProperties properties = basePairForConnector as IExtensibleProperties;
                            //If the base pair entity does not support extensible properties, there is a bug as LoadStructureModel should be using Bio.Data.Providers.Structure.RNASecondaryStructureBasePair objects
                            Debug.Assert(properties != null);
                            BasePairMetadata metadata = null;
                            if (properties.DoesExtendedPropertyExist(BasePairMetadata.Key))
                            {
                                metadata = properties.GetExtendedProperty<BasePairMetadata>(BasePairMetadata.Key);
                            }
                            else
                            {
                                metadata = new BasePairMetadata();
                                properties.SetExtendedProperty(BasePairMetadata.Key, metadata);
                            }
                            metadata.ConnectorVisible = true;
                            metadata.ConnectorRendering = basePairConnector.ConnectorVisual;
                            if (basePairConnector.Style.HasValue)
                                metadata.ConnectorStyle = basePairConnector.Style.Value;
                        }
                    }
                }
            }

            //2. Determine if any base pairs have been specified to not have their connector visible
            var hiddenBasePairs = _lines.Where(l => Regex.IsMatch(l, BPConnectorHiddenDefinition));
            foreach (var hiddenBasePairDef in hiddenBasePairs)
            {
                string[] tokens = Regex.Split(hiddenBasePairDef, " ");
                string[] basePairsToHideRanges = Regex.Split(Regex.Split(tokens[0], "#")[0], ",");
                var basePairsToHide = from token in basePairsToHideRanges
                                      from fpIdx in XRNAFile.ExpandXRNARange(token, true)
                                      from bp in _basePairs
                                      where bp.FivePrimeIndex == (fpIdx - 1)
                                      select bp;
                foreach (var bp in basePairsToHide)
                {
                    IExtensibleProperties properties = bp as IExtensibleProperties;
                    //If the base pair entity does not support extensible properties, there is a bug as LoadStructureModel should be using Bio.Data.Providers.Structure.RNASecondaryStructureBasePair objects
                    Debug.Assert(properties != null);
                    BasePairMetadata metadata = null;
                    if (properties.DoesExtendedPropertyExist(BasePairMetadata.Key))
                    {
                        metadata = properties.GetExtendedProperty<BasePairMetadata>(BasePairMetadata.Key);
                    }
                    else
                    {
                        metadata = new BasePairMetadata();
                        properties.SetExtendedProperty(BasePairMetadata.Key, metadata);
                    }
                    metadata.ConnectorVisible = false;
                }
            }

            //3. Determine if any base pairs have nucleotides that are hidden, we should hide the connector too.
            var basePairsWithHiddenNt = from nt in _sequence.SequenceData.Select((symbol, idx) =>
                                                    {
                                                        IExtensibleProperties properties = symbol as IExtensibleProperties;
                                                        //If the IBioSymbol does not support extensible properties, there is a bug somewhere in the XRNA parser as the XRNABioSequenceVector should set that up.
                                                        Debug.Assert(properties != null);
                                                        //If the NucleotideMetadata object is not there, then there is a bug in the XRNA parser as the XRNABioSequenceVector should set that up.
                                                        Debug.Assert(properties.DoesExtendedPropertyExist(NucleotideMetadata.Key));
                                                        NucleotideMetadata symbolMetadata = properties.GetExtendedProperty<NucleotideMetadata>(NucleotideMetadata.Key);
                                                        return new { NucleotideIndex = idx, Visibility = symbolMetadata.Visible};
                                                    })
                                        from bp in _basePairs
                                        where (bp.ThreePrimeIndex == nt.NucleotideIndex || bp.FivePrimeIndex == nt.NucleotideIndex) && nt.Visibility == false
                                        select bp;
            foreach(var basePairWithHiddenNt in basePairsWithHiddenNt)
            {
                IExtensibleProperties properties = basePairWithHiddenNt as IExtensibleProperties;
                //If the base pair entity does not support extensible properties, there is a bug as LoadStructureModel should be using Bio.Data.Providers.Structure.RNASecondaryStructureBasePair objects
                Debug.Assert(properties != null);
                BasePairMetadata metadata = null;
                if (properties.DoesExtendedPropertyExist(BasePairMetadata.Key))
                {
                    metadata = properties.GetExtendedProperty<BasePairMetadata>(BasePairMetadata.Key);
                }
                else
                {
                    metadata = new BasePairMetadata();
                    properties.SetExtendedProperty(BasePairMetadata.Key, metadata);
                }
                metadata.ConnectorVisible = false;
            }
        }

        /// <summary>
        /// In this method, we determine if any font information has been specified in the XRNA file.
        /// For nucleotides that don't have any font information, we will assign the default text shape.
        /// If any nucleotides are specified to be invisible, we will set the visibility flag appropriately.
        /// </summary>
        /// <returns></returns>
        private void HandleSequenceElements()
        {
            foreach (string line in _lines)
            {
                var ntShape = XRNANucleotideShape.TestCreateDefaultText(line);
                if (ntShape != null)
                {
                    Debug.Assert(ntShape.AssignedTo != null); //If this specific shape was created, we should have some nucleotides to assign it to.
                    Debug.Assert(ntShape.Visual != null); //We should have a specific shape to use for the nucleotides specified in assignedTo
                    Debug.Assert(ntShape.RenderingStyle == NucleotideMetadata.RenderingMode.Text);

                    ITextShape defaultNtVisual = ntShape.Visual as ITextShape;
                    Debug.Assert(defaultNtVisual != null);

                    if (ntShape.AssignedTo.Count() == _sequence.SequenceData.Count) //Then we want to put it in the StructureModelMetadata
                    {
                        _strModelMetadata.NucleotideTextRendering = defaultNtVisual;
                        //_strModelMetadata.NucleotideRenderingMode = NucleotideMetadata.RenderingMode.Text;
                    }
                    else
                    {
                        //Its a text rendering definition applied to a subset
                        foreach (int ntPos in ntShape.AssignedTo)
                        {
                            int idx = ntPos;
                            if (idx < 0 || idx >= _sequence.SequenceData.Count) continue; //An invalid nt specifier
                            IBioSymbol symbol = _sequence.SequenceData[idx];
                            IExtensibleProperties properties = symbol as IExtensibleProperties;
                            //If the IBioSymbol does not support extensible properties, there is a bug somewhere in the XRNA parser as the XRNABioSequenceVector should set that up.
                            Debug.Assert(properties != null);
                            //If the NucleotideMetadata object is not there, then there is a bug in the XRNA parser as the XRNABioSequenceVector should set that up.
                            Debug.Assert(properties.DoesExtendedPropertyExist(NucleotideMetadata.Key));
                            NucleotideMetadata symbolMetadata = properties.GetExtendedProperty<NucleotideMetadata>(NucleotideMetadata.Key);
                            symbolMetadata.Reset();
                            symbolMetadata.IsSubstituted = false;
                            symbolMetadata.Style = ntShape.RenderingStyle;
                            symbolMetadata.TextRendering = defaultNtVisual;
                        }
                    }
                    continue;
                }

                var ntCharSubstitution = XRNANucleotideShape.TestCreateTextSubstitution(line);
                if (ntCharSubstitution != null)
                {
                    Debug.Assert(ntCharSubstitution.AssignedTo != null);
                    Debug.Assert(ntCharSubstitution.Visual != null);
                    Debug.Assert(ntCharSubstitution.IsTextSubstitution);
                    Debug.Assert(ntCharSubstitution.RenderingStyle == NucleotideMetadata.RenderingMode.Text);

                    ITextShape ntCharSubVisual = ntCharSubstitution.Visual as ITextShape;
                    Debug.Assert(ntCharSubVisual != null);

                    foreach (int ntPos in ntCharSubstitution.AssignedTo)
                    {
                        int idx = ntPos;
                        if (idx < 0 || idx >= _sequence.SequenceData.Count) continue; //An invalid nt specifier
                        IBioSymbol symbol = _sequence.SequenceData[idx];
                        IExtensibleProperties properties = symbol as IExtensibleProperties;
                        //If the IBioSymbol does not support extensible properties, there is a bug somewhere in the XRNA parser as the XRNABioSequenceVector should set that up.
                        Debug.Assert(properties != null);
                        //If the NucleotideMetadata object is not there, then there is a bug in the XRNA parser as the XRNABioSequenceVector should set that up.
                        Debug.Assert(properties.DoesExtendedPropertyExist(NucleotideMetadata.Key));
                        NucleotideMetadata symbolMetadata = properties.GetExtendedProperty<NucleotideMetadata>(NucleotideMetadata.Key);
                        symbolMetadata.Reset();
                        symbolMetadata.IsSubstituted = true;
                        symbolMetadata.SubstitutionCharacter = ntCharSubstitution.TextSubstitution;
                        symbolMetadata.Style = ntCharSubstitution.RenderingStyle;
                        symbolMetadata.TextRendering = ntCharSubVisual;
                    }
                    continue;
                }

                var ntSymbolSubstitution = XRNANucleotideShape.TestCreateSymbolSubstitution(line);
                if (ntSymbolSubstitution != null)
                {
                    Debug.Assert(ntSymbolSubstitution.AssignedTo != null);
                    Debug.Assert(ntSymbolSubstitution.Visual != null);
                    Debug.Assert(ntSymbolSubstitution.IsTextSubstitution);
                    Debug.Assert(ntSymbolSubstitution.RenderingStyle == NucleotideMetadata.RenderingMode.Text);

                    ITextShape ntSymbolSubVisual = ntSymbolSubstitution.Visual as ITextShape;
                    Debug.Assert(ntSymbolSubVisual != null);

                    foreach (int ntPos in ntSymbolSubstitution.AssignedTo)
                    {
                        int idx = ntPos;
                        if (idx < 0 || idx >= _sequence.SequenceData.Count) continue; //An invalid nt specifier
                        IBioSymbol symbol = _sequence.SequenceData[idx];
                        IExtensibleProperties properties = symbol as IExtensibleProperties;
                        //If the IBioSymbol does not support extensible properties, there is a bug somewhere in the XRNA parser as the XRNABioSequenceVector should set that up.
                        Debug.Assert(properties != null);
                        //If the NucleotideMetadata object is not there, then there is a bug in the XRNA parser as the XRNABioSequenceVector should set that up.
                        Debug.Assert(properties.DoesExtendedPropertyExist(NucleotideMetadata.Key));
                        NucleotideMetadata symbolMetadata = properties.GetExtendedProperty<NucleotideMetadata>(NucleotideMetadata.Key);
                        symbolMetadata.Reset();
                        symbolMetadata.IsSubstituted = true;
                        symbolMetadata.SubstitutionCharacter = ntSymbolSubstitution.TextSubstitution;
                        symbolMetadata.Style = ntSymbolSubstitution.RenderingStyle;
                        symbolMetadata.TextRendering = ntSymbolSubVisual;
                    }
                    continue;
                }

                var ntCircleSubstitution = XRNANucleotideShape.TestCreateCircleSubstitution(line);
                if (ntCircleSubstitution != null)
                {
                    Debug.Assert(ntCircleSubstitution.AssignedTo != null);
                    Debug.Assert(ntCircleSubstitution.Visual != null);
                    Debug.Assert(ntCircleSubstitution.RenderingStyle == NucleotideMetadata.RenderingMode.Circle);

                    ICircleShape ntSymbolCircleVisual = ntCircleSubstitution.Visual as ICircleShape;
                    Debug.Assert(ntSymbolCircleVisual != null);

                    if (ntCircleSubstitution.AssignedTo.Count() == _sequence.SequenceData.Count) //Then we want to put it in the StructureModelMetadata
                    {
                        _strModelMetadata.NucleotideCircleRendering = ntSymbolCircleVisual;
                    }
                    else
                    {
                        foreach (int ntPos in ntCircleSubstitution.AssignedTo)
                        {
                            int idx = ntPos;
                            if (idx < 0 || idx >= _sequence.SequenceData.Count) continue; //An invalid nt specifier
                            IBioSymbol symbol = _sequence.SequenceData[idx];
                            IExtensibleProperties properties = symbol as IExtensibleProperties;
                            //If the IBioSymbol does not support extensible properties, there is a bug somewhere in the XRNA parser as the XRNABioSequenceVector should set that up.
                            Debug.Assert(properties != null);
                            //If the NucleotideMetadata object is not there, then there is a bug in the XRNA parser as the XRNABioSequenceVector should set that up.
                            Debug.Assert(properties.DoesExtendedPropertyExist(NucleotideMetadata.Key));
                            NucleotideMetadata symbolMetadata = properties.GetExtendedProperty<NucleotideMetadata>(NucleotideMetadata.Key);
                            symbolMetadata.Reset();
                            symbolMetadata.IsSubstituted = false;
                            symbolMetadata.Style = ntCircleSubstitution.RenderingStyle;
                            symbolMetadata.CircleRendering = ntSymbolCircleVisual;
                        }
                    }
                    continue;
                }
            }

            var hiddenNtSetDefs = _lines.Where(l => Regex.IsMatch(l, NTHiddenDefinition));
            foreach (var hiddenNtSet in hiddenNtSetDefs)
            {
                string[] tokens = Regex.Split(hiddenNtSet, " ");
                Debug.Assert(tokens.Length % 2 == 0 && tokens.Length >= 4);
                int startIdx, endIdx;

                int i = 2;
                while (i <= (tokens.Length - 2))
                {
                    if (Int32.TryParse(tokens[i], out startIdx) &&
                        Int32.TryParse(tokens[i + 1], out endIdx) &&
                        startIdx <= endIdx &&
                        (startIdx - 1) >= 0 &&
                        (endIdx - 1) < _sequence.SequenceData.Count)
                    {
                        IEnumerable<int> hiddenNt = XRNAFile.ExpandXRNARange(string.Format("{0}-{1}", startIdx - 1, endIdx - 1), true);
                        foreach (var ntIdx in hiddenNt)
                        {
                            IBioSymbol symbol = _sequence.SequenceData[ntIdx];
                            IExtensibleProperties properties = symbol as IExtensibleProperties;
                            //If the IBioSymbol does not support extensible properties, there is a bug somewhere in the XRNA parser as the XRNABioSequenceVector should set that up.
                            Debug.Assert(properties != null);
                            //If the NucleotideMetadata object is not there, then there is a bug in the XRNA parser as the XRNABioSequenceVector should set that up.
                            Debug.Assert(properties.DoesExtendedPropertyExist(NucleotideMetadata.Key));
                            NucleotideMetadata symbolMetadata = properties.GetExtendedProperty<NucleotideMetadata>(NucleotideMetadata.Key);
                            symbolMetadata.Visible = false;
                        }
                    }
                    i = i + 2;
                }
            }
        }

        /// <summary>
        /// In this method we load the structure model and the absolute rendering map from the XRNA file. 
        /// </summary>
        /// <returns>false if we fail parsing</returns>
        private bool LoadStructureModel()
        {
            Dictionary<int, int> pairings = new Dictionary<int, int>();
            //1. Pickup the basic sequence elements, base pairs and rendering instructions
            foreach (string line in _lines)
            {
                int index, pairedIndex;
                var renderingVector = XRNABioSequenceVector.Create(line, out index, out pairedIndex);
                if (renderingVector != null) _rendering.RenderingMap.Add(index, renderingVector);
                if (pairedIndex > -1 && index < pairedIndex)
                {
                    if(!pairings.ContainsKey(index) && 
                       !pairings.ContainsValue(pairedIndex) &&
                       !pairings.ContainsKey(pairedIndex) &&
                       !pairings.ContainsValue(index)) { pairings.Add(index, pairedIndex); }
                    else { return false; } //Consistency error as a position is involved in more than two base pairs
                }
            }

            if (_rendering.RenderingMap.Keys.Count <= 0) return false; //I don't know what this is as its not an XRNA Structure Model.
            
            //2. Setup the sequence object
            var orderedByIndex = _rendering.RenderingMap.Keys.OrderBy(key => key);
            foreach (var index in orderedByIndex)
            {
                _sequence.AddSymbol(((XRNABioSequenceVector)_rendering.RenderingMap[index]).BioSymbol);
            }

            if(_rendering.RenderingMap.Keys.Count != _sequence.SequenceData.Count) return false; //Error, we have an invalid nucleotide definition somewhere

            //3. Connect consecutive vectors on the rendering map
            for (int i = 0; i < _sequence.SequenceData.Count; i++)
            {
                XRNABioSequenceVector currentPosition = (XRNABioSequenceVector)_rendering.RenderingMap[i];
                if (i < (_sequence.SequenceData.Count - 1)) //The last nucleotide is unconnected, so we check for that
                {
                    if (!_rendering.RenderingMap.ContainsKey(i + 1)) return false; //Error, we are missing the definition for a consecutive nt.
                    XRNABioSequenceVector nextPosition = (XRNABioSequenceVector)_rendering.RenderingMap[i + 1];
                    currentPosition.ConnectTo(nextPosition);
                }
            }

            //4. Initialize base pairs and extents
            foreach(var fivePrimeIdx in pairings.Keys) { _basePairs.Add(new RNASecondaryStructureBasePair(_sequence, fivePrimeIdx, pairings[fivePrimeIdx])); }
            try
            {
                RNAParser labelRNAParser = new RNAParser(pairings, _sequence.SequenceData.Count);
                XElement extentsXML = labelRNAParser.ParseToExtents();
                var extents = from extent in extentsXML.Descendants("Extent")
                              select new RNASecondaryStructureExtent(_sequence,
                                                                   Int32.Parse(extent.Element("ExtentID").Value),
                                                                   Int32.Parse(extent.Element("ExtentOrdinal").Value),
                                                                   (StructureExtentType)Enum.Parse(typeof(StructureExtentType), extent.Element("ExtentTypeID").Value, true),
                                                                   Int32.Parse(extent.Element("ExtentStartIndex").Value),
                                                                   Int32.Parse(extent.Element("ExtentEndIndex").Value));
                _extents.AddRange(extents);
            }
            catch (Exception) //Some error parsing the extents, they won't be available.
            {
            }
            return true;
        }

        private StructureModelMetadata _strModelMetadata;
        private SequenceRendering _rendering;
        private SimpleRNASequence _sequence;
        private List<IBasePairBioEntity> _basePairs;
        private List<IStructureExtentBioEntity> _extents;
        private List<string> _lines;

        private const string BPConnectorHiddenDefinition = @"[\d+(-?\d+),?]+\s#!Bn";
        private const string NTHiddenDefinition = @"^#!\sh\s";

        #endregion

        #region Internals

        internal struct XRNATypeface
        {
            public string Font;
            public string FontStyle;
            public string FontWeight;
        }

        internal static XRNATypeface ParseXRNACode(int xRNACode)
        {
            XRNATypeface retValue;
            switch (xRNACode)
            {
                case 0:
                    retValue.Font = "Arial";
                    retValue.FontStyle = "Normal";
                    retValue.FontWeight = "Regular";
                    break;
                case 1:
                    retValue.Font = "Arial";
                    retValue.FontStyle = "Oblique";
                    retValue.FontWeight = "Regular";
                    break;
                case 2:
                    retValue.Font = "Arial";
                    retValue.FontWeight = "Bold";
                    retValue.FontStyle = "Normal";
                    break;
                case 3:
                    retValue.Font = "Arial";
                    retValue.FontStyle = "Oblique";
                    retValue.FontWeight = "Bold";
                    break;
                case 4:
                    retValue.Font = "Times, Times New Roman";
                    retValue.FontStyle = "Normal";
                    retValue.FontWeight = "Regular";
                    break;
                case 5:
                    retValue.Font = "Times, Times New Roman";
                    retValue.FontStyle = "Italic";
                    retValue.FontWeight = "Regular";
                    break;
                case 6:
                    retValue.Font = "Times, Times New Roman";
                    retValue.FontWeight = "Bold";
                    retValue.FontStyle = "Normal";
                    break;
                case 7:
                    retValue.Font = "Times, Times New Roman";
                    retValue.FontStyle = "Italic";
                    retValue.FontWeight = "Bold";
                    break;
                case 8:
                    retValue.Font = "Courier, Courier New";
                    retValue.FontStyle = "Normal";
                    retValue.FontWeight = "Regular";
                    break;
                case 9:
                    retValue.Font = "Courier, Courier New";
                    retValue.FontStyle = "Oblique";
                    retValue.FontWeight = "Regular";
                    break;
                case 10:
                    retValue.Font = "Courier, Courier New";
                    retValue.FontWeight = "Bold";
                    retValue.FontStyle = "Normal";
                    break;
                case 11:
                    retValue.Font = "Courier, Courier New";
                    retValue.FontStyle = "Oblique";
                    retValue.FontWeight = "Bold";
                    break;
                case 13:
                    retValue.Font = "Symbol";
                    retValue.FontStyle = "Normal";
                    retValue.FontWeight = "Regular";
                    break;
                default:
                    retValue.Font = "Arial";
                    retValue.FontStyle = "Normal";
                    retValue.FontWeight = "Regular";
                    break;
            };
            return retValue;
        }

        internal static IEnumerable<int> ExpandXRNARange(string rangeDef, bool inclusive)
        {
            if (!rangeDef.Contains("-"))
            {
                yield return Int32.Parse(rangeDef);
            }
            else
            {
                string[] ends = Regex.Split(rangeDef, "-");
                int start = Int32.Parse(ends[0]), end = Int32.Parse(ends[1]), count = end - start;
                foreach (int val in Enumerable.Range(start, (inclusive) ? count + 1 : count))
                {
                    yield return val;
                }
            }
        }

        #endregion
    }
}